package parser;


import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import entity.nvdcve.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.JsonFileUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * 解析nvd-cve的json数据源，json schema版本为1.1
 * json schema地址：https://csrc.nist.gov/schema/nvd/feed/1.1/nvd_cve_feed_json_1.1.schema
 *
 * @author xym
 * @since 2020/7/15
 */
public class NvdJsonParser {
    private static final Logger log = LoggerFactory.getLogger(NvdJsonParser.class);

    /* ===========以下四个数组用于定义base metric中需要提取的字段============= */

    private static final String[] BASE_METRIC_V2_ITEMS = new String[]{
            "severity", "exploitabilityScore", "impactScore",
            "acInsufInfo", "obtainAllPrivilege", "obtainUserPrivilege",
            "obtainOtherPrivilege", "userInteractionRequired"
    };

    private static final String[] CVSS_V2_ITEMS = new String[]{
            "version", "vectorString", "accessVector", "accessComplexity",
            "authentication", "confidentialityImpact", "integrityImpact",
            "availabilityImpact", "baseScore"
    };

    private static final String[] BASE_METRIC_V3_ITEMS = new String[]{
            "exploitabilityScore", "impactScore"
    };

    private static final String[] CVSS_V3_ITEMS = new String[]{
            "version", "vectorString", "attackVector",
            "attackComplexity", "privilegesRequired", "userInteraction",
            "scope", "confidentialityImpact", "integrityImpact",
            "availabilityImpact", "baseScore", "baseSeverity"
    };

    /* ===========以上四个数组用于定义base metric中需要提取的字段============= */

    /**
     * 对外暴露的解析接口，接收源json文件输入，返回json文件中包含的漏洞信息
     *
     * @param fileName json源文件
     * @return 漏洞信息
     */
    public List<Vulnerability> execute(String fileName) {
        if (!(fileName.endsWith("json") || fileName.endsWith("JSON"))) {
            log.info("{}格式不正确", fileName);
            throw new RuntimeException("文件格式不正确");
        }

        JsonObject rawData = JsonFileUtil.read(fileName);
        int recordNumber = rawData.get("CVE_data_numberOfCVEs").getAsInt();
        log.info("文件共计{}条cve记录", recordNumber);

        long start = System.currentTimeMillis();

        List<Vulnerability> vulnerabilityList = new ArrayList<>();
        JsonArray cveItems = rawData.get("CVE_Items").getAsJsonArray();
        for (int i = 0; i < cveItems.size(); i++) {
            JsonObject cveItem = cveItems.get(i).getAsJsonObject();
            Vulnerability vulnerability = parse(cveItem);
            vulnerabilityList.add(vulnerability);
        }

        long end = System.currentTimeMillis();
        log.info("{}条cve记录解析完成，耗时{}ms", vulnerabilityList.size(), (end - start));

        return vulnerabilityList;
    }

    /**
     * 封装了解析各个字段的结果
     *
     * @param cveItem 字段"CVE_ITEMS"下的所有数据
     * @return 解析结果
     */
    private Vulnerability parse(JsonObject cveItem) {
        Vulnerability vulnerability = new Vulnerability();

        JsonObject cve = cveItem.get("cve").getAsJsonObject();
        innerCveParse(cve, vulnerability);

        JsonObject impact = cveItem.get("impact").getAsJsonObject();
        innerImpactParse(impact, vulnerability);

        JsonObject configurations = cveItem.get("configurations").getAsJsonObject();
        innerConfigurationsParse(configurations, vulnerability);

        /*填充字段publishedDate*/
        String publishedDate = cveItem.get("publishedDate").getAsString();
        vulnerability.setPublishedDate(publishedDate);

        /*填充字段lastModifiedDate*/
        String lastModifiedDate = cveItem.get("lastModifiedDate").getAsString();
        vulnerability.setLastModifiedDate(lastModifiedDate);

        return vulnerability;
    }

    /**
     * 解析字段"cve"下的所有数据
     *
     * @param cve           字段CVE_Items.cve
     * @param vulnerability 解析结果
     */
    private void innerCveParse(JsonObject cve, Vulnerability vulnerability) {
        /*读取字段cveId*/
        String cveId = cve.get("CVE_data_meta").getAsJsonObject().get("ID").getAsString();
        vulnerability.setCveId(cveId);

        /*读取字段problemType*/
        JsonArray problemTypeList = cve.get("problemtype").getAsJsonObject().get("problemtype_data").getAsJsonArray();
        for (int j = 0; j < problemTypeList.size(); j++) {
            JsonObject problemType = problemTypeList.get(j).getAsJsonObject();

            //description(cwe_id)
            JsonArray cweList = problemType.get("description").getAsJsonArray();
            if (cweList.size() > 0) {
                List<String> relatedCwes = new ArrayList<>();
                for (int k = 0; k < cweList.size(); k++) {
                    JsonObject description = cweList.get(k).getAsJsonObject();
                    String cweId = description.get("value").getAsString();
                    relatedCwes.add(cweId);
                }
                vulnerability.setRelatedCwes(relatedCwes);
            }
        }

        /*读取reference字段*/
        JsonArray referenceList = cve.get("references").getAsJsonObject().get("reference_data").getAsJsonArray();
        if (referenceList.size() > 0) {
            List<Reference> references = new ArrayList<>();
            for (int j = 0; j < referenceList.size(); j++) {
                Reference cveReference = new Reference();

                JsonObject reference = referenceList.get(j).getAsJsonObject();

                String url = reference.get("url").getAsString();
                cveReference.setUrl(url);

                String refSource = reference.get("refsource").getAsString();
                cveReference.setRefsource(refSource);

                String name = reference.get("name").getAsString();
                cveReference.setName(name);

                //reference tags
                JsonArray referenceTagList = reference.get("tags").getAsJsonArray();
                if (referenceTagList.size() > 0) {
                    List<String> tags = new ArrayList<>();
                    for (int i = 0; i < referenceTagList.size(); i++) {
                        tags.add(referenceTagList.get(i).getAsString());
                    }
                    cveReference.setTags(tags);
                }

                references.add(cveReference);
            }
            vulnerability.setReferences(references);
        }

        /*读取字段description*/
        JsonArray descriptionList = cve.get("description").getAsJsonObject().get("description_data").getAsJsonArray();
        if (descriptionList.size() > 0) {
            StringBuilder descriptionValue = new StringBuilder();
            for (int j = 0; j < descriptionList.size(); j++) {
                JsonObject description = descriptionList.get(j).getAsJsonObject();
                descriptionValue.append(description.get("value").getAsString());
            }
            vulnerability.setDescription(descriptionValue.toString());
        }
    }

    /**
     * 解析字段"configurations"下的所有信息
     *
     * @param configurations 字段CVE_Items.configurations
     * @param vulnerability  解析结果
     */
    private void innerConfigurationsParse(JsonObject configurations, Vulnerability vulnerability) {
        JsonArray nodes = configurations.get("nodes").getAsJsonArray();
        List<BaseProduct> productList = new ArrayList<>();
        innerNodeParse(nodes, productList);
        vulnerability.setAffectedProducts(productList);
    }

    /**
     * 解析字段"configurations"下的字段"nodes"
     *
     * @param nodes       字段configurations.nodes
     * @param productList 受该漏洞影响的产品
     */
    private void innerNodeParse(JsonArray nodes, List<BaseProduct> productList) {
        for (int i = 0; i < nodes.size(); i++) {
            JsonObject node = nodes.get(i).getAsJsonObject();
            if (node.get("children") != null) {
                innerNodeParse(node.get("children").getAsJsonArray(), productList);
                continue;
            }
            if (node.get("cpe_match") != null) {
                JsonArray cpeLists = node.get("cpe_match").getAsJsonArray();
                for (int j = 0; j < cpeLists.size(); j++) {
                    JsonObject cpe = cpeLists.get(j).getAsJsonObject();
                    if (cpe.get("vulnerable").getAsBoolean()) {
                        String cpeUri = cpe.get("cpe23Uri").getAsString();
                        if (cpeUri != null) {
                            CpeUriParser cpeParser = CpeUriParser.getParser();
                            BaseProduct product = cpeParser.execute(cpeUri);
                            productList.add(product);
                        }
                    }
                }
            }
        }
    }

    /**
     * 解析字段"impact"下的所有信息
     *
     * @param impactItem    字段CVE_Items.impact
     * @param vulnerability 解析结果
     */
    private void innerImpactParse(JsonObject impactItem, Vulnerability vulnerability) {
        Impact impact = new Impact();
        /*读取字段baseMetricV2*/
        if (impactItem.get("baseMetricV2") != null) {
            JsonObject baseMetricV2Item = impactItem.get("baseMetricV2").getAsJsonObject();
            if (baseMetricV2Item != null) {
                BaseMetricV2 baseMetricV2 = new BaseMetricV2();
                for (int i = 0; i < BASE_METRIC_V2_ITEMS.length; i++) {
                    setField(baseMetricV2Item, BASE_METRIC_V2_ITEMS[i], baseMetricV2);
                }

                /*抽取baseMetric中的cvss*/
                JsonObject cvssV2Item = baseMetricV2Item.get("cvssV2").getAsJsonObject();
                if (cvssV2Item != null) {
                    CvssV2 cvssV2 = new CvssV2();
                    for (int i = 0; i < CVSS_V2_ITEMS.length; i++) {
                        setField(cvssV2Item, CVSS_V2_ITEMS[i], cvssV2);
                    }
                    baseMetricV2.setCvssV2(cvssV2);
                }

                impact.setBaseMetricV2(baseMetricV2);
            }
        }

        /*读取字段baseMetricV3*/
        if (impactItem.get("baseMetricV3") != null) {
            JsonObject baseMetricV3Item = impactItem.get("baseMetricV3").getAsJsonObject();
            if (baseMetricV3Item != null) {
                BaseMetricV3 baseMetricV3 = new BaseMetricV3();
                for (int i = 0; i < BASE_METRIC_V3_ITEMS.length; i++) {
                    setField(baseMetricV3Item, BASE_METRIC_V3_ITEMS[i], baseMetricV3);
                }

                /*抽取baseMetric中的cvss*/
                JsonObject cvssV3Item = baseMetricV3Item.get("cvssV3").getAsJsonObject();
                if (cvssV3Item != null) {
                    CvssV3 cvssV3 = new CvssV3();
                    for (int i = 0; i < CVSS_V3_ITEMS.length; i++) {
                        setField(cvssV3Item, CVSS_V3_ITEMS[i], cvssV3);
                    }
                    baseMetricV3.setCvssV3(cvssV3);
                }

                impact.setBaseMetricV3(baseMetricV3);
            }
        }

        vulnerability.setImpact(impact);
    }

    /**
     * 使用反射来设置目标对象域的值
     *
     * @param jsonObject json对象
     * @param itemName   json对象中子项的名称
     * @param target     目标对象
     */
    private void setField(JsonObject jsonObject, String itemName, Object target) {
        JsonElement jsonElement = jsonObject.get(itemName);
        if (jsonElement != null) {
            String value = jsonElement.getAsString();
            try {
                Field field = target.getClass().getDeclaredField(itemName);
                field.setAccessible(true);
                field.set(target, field.getType().getConstructor(String.class).newInstance(value));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
